package com.atguigu.gmall.order.service.Impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.cart.client.CartFeignClient;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @Author atguigu-mqx
 * @Description TODO
 * @Date 2023/5/23 15:47
 * @Version 1.0
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper,OrderInfo> implements OrderService  {

    @Autowired
    private CartFeignClient cartFeignClient;

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderService orderService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${ware.url}")
    private String wareUrl; // http://localhost:9001

    @Autowired
    private RabbitService rabbitService;

    @Override
    public Map<String, Object> trade(String userId) {
        //  声明一个map
        HashMap<String, Object> hashMap = new HashMap<>();
        //  根据userId 获取到选中的商品列表.
        List<CartInfo> cartCheckedList = this.cartFeignClient.getCartCheckedList(userId);
        //  声明一个总件数变量 AtomicInteger -- 原子操作类，JUC  CAS -- AQS
        AtomicInteger totalNum = new AtomicInteger();
        List<OrderDetail> detailArrayList = new ArrayList<>();
        //  转换为 orderDetail 集合
        if (!CollectionUtils.isEmpty(cartCheckedList)){
            //  使用stream 流
             detailArrayList = cartCheckedList.stream().map(cartInfo -> {
                OrderDetail orderDetail = new OrderDetail();
                //  提交数据的时候，需要skuId
                orderDetail.setSkuId(cartInfo.getSkuId());
                //  以下四个字段是页面渲染时需要使用的！
                orderDetail.setSkuName(cartInfo.getSkuName());
                orderDetail.setSkuNum(cartInfo.getSkuNum());
                orderDetail.setOrderPrice(cartInfo.getSkuPrice());
                orderDetail.setImgUrl(cartInfo.getImgUrl());

                totalNum.addAndGet(cartInfo.getSkuNum());
                //  返回数据
                return orderDetail;
            }).collect(Collectors.toList());
            //  保存送货清单
            hashMap.put("detailArrayList",detailArrayList);
        }
        //  totalNum
        hashMap.put("totalNum",totalNum);
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setOrderDetailList(detailArrayList);
        //  计算总金额的方法.
        orderInfo.sumTotalAmount();
        //  总金额：
        hashMap.put("totalAmount",orderInfo.getTotalAmount());
        //  存储流水号
        hashMap.put("tradeNo",orderService.getTradeNo(userId));
        //  返回数据
        return hashMap;
    }

    /**
     * 保存订单
     * @param orderInfo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        //  order_info;
        //  total_amount order_status user_id out_trade_no trade_body operate_time expire_time process_status
        //  给以上字段进行赋值.
        orderInfo.sumTotalAmount();
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //  out_trade_no 第三方支付的交易编号. 不能重复，必须唯一。
        String outTradeNo = "ATGUIGU"+System.currentTimeMillis()+new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        //  订单的描述信息.
        orderInfo.setTradeBody("硅谷专卖店");
        orderInfo.setOperateTime(new Date());

        //  设置过期时间 +1天;  Calendar 线程不安全的。
        //        Calendar calendar = Calendar.getInstance();
        //        calendar.add(Calendar.DATE,1);
        //        orderInfo.setExpireTime(calendar.getTime());
        ZonedDateTime zonedDateTime = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
        Date date = Date.from(zonedDateTime.toInstant());
        orderInfo.setExpireTime(date);

        //  赋值进度状态.
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        //  保存数据
        this.orderInfoMapper.insert(orderInfo);
        //  order_detail;
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)){
            //  循环遍历插入数据
            for (OrderDetail orderDetail : orderDetailList) {
                //  赋值一个订单Id
                orderDetail.setOrderId(orderInfo.getId());
                orderDetailMapper.insert(orderDetail);
            }
        }
        //  获取订单Id
        Long orderId = orderInfo.getId();
        //  发送一个延迟消息：
        this.rabbitService.sendDelayMsg(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,MqConst.ROUTING_ORDER_CANCEL,orderId,MqConst.DELAY_TIME);
        //  返回订单id
        return orderId;
    }

    @Override
    public String getTradeNo(String userId) {
        //  获取流水号
        String tradeNo = UUID.randomUUID().toString();
        //  声明一个key
        String tradeNoKey = "tradeNo:"+userId;
        this.redisTemplate.opsForValue().set(tradeNoKey,tradeNo);
        //  返回流水号
        return tradeNo;
    }

    @Override
    public Boolean checkTradeNo(String tradeNo, String userId) {
        //  声明一个key
        String tradeNoKey = "tradeNo:"+userId;
        String redisTradeNo = (String) this.redisTemplate.opsForValue().get(tradeNoKey);
        //  返回比较结果
        return tradeNo.equals(redisTradeNo);
    }

    @Override
    public void delTradeNo(String userId) {
        //  del key;
        this.redisTemplate.delete("tradeNo:"+userId);
    }

    @Override
    public Boolean hasStock(Long skuId, Integer skuNum) {
        //  1.  如何获取到这个接口地址：    远程调用 feign; --> 必须spring cloud; httpClientUtil
        //  2.  接口传递的参数：skuId，skuNum;
        //  3.  获取到接口的返回值:
        //  4.  根据你返回值结果， 0--false 1--true
        // http://localhost:9001/hasStock?skuId=10221&num=2
        String result = HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum);
        //  返回结果
        return "1".equals(result);
    }

    @Override
    public IPage<OrderInfo> getOrderPage(Page<OrderInfo> orderInfoPage, String userId, String orderStatus) {
        //  调用mapper层.
        IPage<OrderInfo> infoIPage =  orderInfoMapper.selectOrderPage(orderInfoPage,userId,orderStatus);
        //  获取到订单状态：orderStatusName
        infoIPage.getRecords().forEach(orderInfo -> {
            //  通过状态获取到的状态的名称
            orderInfo.setOrderStatusName(OrderStatus.getStatusNameByStatus(orderInfo.getOrderStatus()));
        });
        return infoIPage;
    }

    @Override
    public void execExpiredOrder(Long orderId) {
        //  取消订单本质修改订单状态. 根据订单Id 关闭订单; 根据订单Id , 修改订单状态为 PAID;   根据订单Id , 修改订单状态为 FINISHED;
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        //        orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
        //        orderInfoMapper.updateById(orderInfo);
        //  根据订单Id ，修改订单状态。
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);
        //        this.updateOrderStatusById(orderId,ProcessStatus.PAID);
        //        this.updateOrderStatusById(orderId,ProcessStatus.SPLIT);
        //        this.updateOrderStatusById(orderId,ProcessStatus.FINISHED);
        //  发送消息关闭本地交易记录.paymentInfo;
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
    }

    /**
     * 根据订单Id 修改订单状态。
     * @param orderId
     * @param processStatus
     */
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setId(orderId);
            orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
            orderInfo.setProcessStatus(processStatus.name());
            orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //  获取定对象
        OrderInfo orderInfo = this.getById(orderId);
        //  获取订单明细.
        if (null != orderInfo){
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderId));
            orderInfo.setOrderDetailList(orderDetailList);
        }
        return orderInfo;
    }

    @Override
    public void sendMsgToWare(Long orderId) {
        //  修改订单状态,
        this.updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);
        //  发送消息：
        //  获取发送消息数据
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        //  需要封装发送的数据.
        Map<String,Object> map = this.initWare(orderInfo);
        this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK, JSONObject.toJSONString(map));
    }

    /**
     * 将orderInfo 部分数据转化为map 集合
     * @param orderInfo
     * @return
     */
    public Map<String, Object> initWare(OrderInfo orderInfo) {
        //  创建map 集合
        Map<String, Object> map = new HashMap<>();
        //  给map 赋值
        map.put("orderId",orderInfo.getId());
        map.put("consignee",orderInfo.getConsignee());
        map.put("consigneeTel", orderInfo.getConsigneeTel());
        map.put("orderComment", orderInfo.getOrderComment());
        map.put("orderBody", orderInfo.getTradeBody());
        map.put("deliveryAddress", orderInfo.getDeliveryAddress());
        map.put("paymentWay", "2");
        //  这个仓库Id 只为仓库拆单使用。
        map.put("wareId", orderInfo.getWareId());

        //  赋值订单明细.
        //  获取订单明细
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //  循环遍历.
        List<HashMap<String, Object>> mapList = orderDetailList.stream().map(orderDetail -> {
            //  创建map 集合
            HashMap<String, Object> detailMap = new HashMap<>();
            detailMap.put("skuId", orderDetail.getSkuId());
            detailMap.put("skuNum", orderDetail.getSkuNum());
            detailMap.put("skuName", orderDetail.getSkuName());

            return detailMap;
        }).collect(Collectors.toList());

        map.put("details",mapList);
        //  返回map 集合
        return map;
    }

    //  wareSkuMap = [{"wareId":"1","skuIds":["23"]},{"wareId":"2","skuIds":["3"]}] List<Map>
    @Override
    public List<OrderInfo> orderSplit(Long orderId, String wareSkuMap) {
        //  声明子订单集合
        List<OrderInfo> orderInfoList = new ArrayList<>();
        //  1.  先获取到原始订单
        OrderInfo orderInfoOrigin = this.getOrderInfo(orderId);
        //  2.  将wareSkuMap 字符串变为能操作的对象.
        List<Map> mapList = JSON.parseArray(wareSkuMap, Map.class);
        //  判断
        if (!CollectionUtils.isEmpty(mapList)){
            //  循环遍历
            for (Map map : mapList) {
                String wareId = (String) map.get("wareId");
                List<String> skuIdsList = (List<String>) map.get("skuIds");
                //  3。 创建子订单对象
                OrderInfo subOrderInfo = new OrderInfo();
                //  4.  进行属性拷贝
                BeanUtils.copyProperties(orderInfoOrigin,subOrderInfo);
                //  5.  将Id 置为null
                subOrderInfo.setId(null);
                //  6.  赋值父订单Id
                subOrderInfo.setParentOrderId(orderId);
                //  7.  赋值仓库Id
                subOrderInfo.setWareId(wareId);
                //  8.  赋值子订单明细. 53: 23,24,25   23-1   24,25-2
                List<OrderDetail> orderDetailList = orderInfoOrigin.getOrderDetailList();
                List<OrderDetail> detailList = orderDetailList.stream().filter(orderDetail -> skuIdsList.contains(orderDetail.getSkuId().toString())).collect(Collectors.toList());
                //  计算价格：
                subOrderInfo.setOrderDetailList(detailList);
                subOrderInfo.sumTotalAmount();
                //  9. 保存子订单
                this.saveOrderInfo(subOrderInfo);
                //  10. 将子订单添加到集合中.
                orderInfoList.add(subOrderInfo);
            }
        }
        //  11。 修改原始订单的状态.
        this.updateOrderStatus(orderId,ProcessStatus.SPLIT);

        return orderInfoList;
    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {
        //  根据订单Id ，修改订单状态。
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);
        //  判断
        if ("2".equals(flag)){
            //  发送消息关闭本地交易记录.paymentInfo;
            this.rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        }

    }

    public static void main(String[] args) {
        //  LocalDate 日期对象
        //  LocalDate 时间对象
        //  LocalDateTime 日期时间对象
        //        System.out.println(LocalDate.now());
        //        System.out.println(LocalTime.now());
        //
        //
        //        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //        System.out.println(simpleDateFormat.format(new Date()));
        //
        //        System.out.println(LocalDateTime.now().plusDays(1));
        //        System.out.println(LocalDateTime.now().plusMinutes(30));
        //
        //        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        //        LocalDateTime parse = LocalDateTime.parse("2019-12-07 21:20:06", dateTimeFormatter);
        //        System.out.println(parse);
        //        System.out.println(dateTimeFormatter.format(parse));

        //        ZonedDateTime zonedDateTime = LocalDateTime.now().atZone(ZoneId.systemDefault());
        //        Date date = Date.from(zonedDateTime.toInstant());
        //        System.out.println(date);
        //        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));

        //        Calendar calendar = Calendar.getInstance();
        //        calendar.add(Calendar.DATE,1);
        //        System.out.println(calendar.getTime());
        //
        //        ZonedDateTime zonedDateTime = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
        //        Date date = Date.from(zonedDateTime.toInstant());
        //        System.out.println(date);
        //        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));

    }
}
